Skip to content

fix(lsp): fix incorrect project dir when configurationPath points to config outside of workspace#9392

Merged
ematipico merged 7 commits intobiomejs:mainfrom
g-ortuno:config-in-separate-dir
Mar 12, 2026
Merged

fix(lsp): fix incorrect project dir when configurationPath points to config outside of workspace#9392
ematipico merged 7 commits intobiomejs:mainfrom
g-ortuno:config-in-separate-dir

Conversation

@g-ortuno
Copy link
Copy Markdown
Contributor

@g-ortuno g-ortuno commented Mar 8, 2026

I used AI to find the issue, write the fix, and an initial version of the test. I ran out of tokens, so I rewrote the test myself (since it wasn't actually verifying anything) and verified it fails without the fix and passes with the fix. I'm mainly a C++ developer so happy to address any idiomatic rust feedback!

Summary

Closed biomejs/biome-zed#108
#9049 fixed #7138 but inadvertently broke projects where configurationPath pointed to a folder outside of the workspace. biomejs/biome-vscode#959 and #9217 are related.

The repro case is something like this:

  1. Have a biome.jsonc file in c:/code/configs/biome.jsonc
  2. Set configurationPath: "c:/code/configs/biome.jsonc" in VSCode/IDE.
  3. Start VSCode in a sibling folder e.g. c:/code/foo_project/

Before #9049, in load_biome_configuration_file_internal, setting configurationPath in VSCode would fall back to the _ case, which would use base_path() as path which is later used to open the project. IIUC, base_path would be the workspace's path.

After #9049, the new branch runs which uses the parent of the configuration file which in the repro case is c:/code/configs/.

I added a log after this function and I see the following, which confirms the above.

[CUSTOM-BUILD] Registering project at path: "c:/code/configs"

I also added a log to text_document.rs in did_open() in session.project_for_path(). So when I try to format a file I see the following in the logs:

[CUSTOM-BUILD] Could not find project for c:/code/foo_project/bad_formatted.ts

The PR fixes this by not using the config's parent as the root of the project; instead if the config's parent is outside the workspaces, use the first workspace or fall back to base_path.

Test Plan

Verified locally with configurationPath pointing to an external config that files in the workspace were formatted. I also added a test that reproduces the issue and verified it passes with the fix and fails without it.

Docs

N/A

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 8, 2026

🦋 Changeset detected

Latest commit: 8bcd697

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added the A-LSP Area: language server protocol label Mar 8, 2026
@g-ortuno g-ortuno changed the title fix(lsp): Fix incorrect project dir when configurationPath points to config outside of workspace fix(lsp): fix incorrect project dir when configurationPath points to config outside of workspace Mar 8, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new ConfigurationPathHint variant FromUserExternal and updates resolution to return hints instead of raw paths. Threads the hint through session resolution, workspace settings loading, service config reading, and text-document handling. Adjusts project registration and path handling to use the new hint semantics and normalises path joins. Adds test helpers and regression tests covering absolute and relative configuration files located outside workspace roots. Includes a changeset recording a patch bump and the fix.

Possibly related PRs

Suggested reviewers

  • ematipico
  • arendjr
  • dyc3
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the main fix: preventing incorrect project directory registration when a configuration file points outside the workspace.
Description check ✅ Passed The PR description clearly relates to the changeset, detailing a regression fix for configuration path resolution when pointing outside the workspace.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/evil-snails-arrive.md:
- Around line 1-13: The changeset front matter uses multiple "minor" entries and
a non-compliant description; update the .changeset/evil-snails-arrive.md front
matter to list only the affected package "@biomejs/biome" with change type
"patch" (remove the extra CLI package entries and change "minor" -> "patch"),
and replace the summary line with the required bug-fix format: start the
description with "Fixed [`#959`](https://github.com/biomejs/biome/issues/959): Fix
incorrect project dir when configurationPath points to config outside of
workspace". Ensure the file now follows the guideline of a single package entry
and a "Fixed [`#NUMBER`](...): ..." description.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5f30e477-d542-46c2-a82f-e72c0a0deb70

📥 Commits

Reviewing files that changed from the base of the PR and between 1bb9edc and c95ea96.

📒 Files selected for processing (3)
  • .changeset/evil-snails-arrive.md
  • crates/biome_lsp/src/server.tests.rs
  • crates/biome_lsp/src/session.rs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
.changeset/evil-snails-arrive.md (2)

1-11: Consider removing the CLI package entries.

The coding guidelines specify "package (@biomejs/biome)" (singular), suggesting only the main package should be listed. Since this LSP fix isn't platform-specific, you might simplify the front matter to just "@biomejs/biome": patch.

♻️ Simplified front matter
 ---
 "@biomejs/biome": patch
-"@biomejs/cli-darwin-arm64": patch
-"@biomejs/cli-darwin-x64": patch
-"@biomejs/cli-linux-arm64": patch
-"@biomejs/cli-linux-arm64-musl": patch
-"@biomejs/cli-linux-x64": patch
-"@biomejs/cli-linux-x64-musl": patch
-"@biomejs/cli-win32-arm64": patch
-"@biomejs/cli-win32-x64": patch
 ---

As per coding guidelines, changeset files should specify the package (singular) and typically only list @biomejs/biome unless there are platform-specific changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/evil-snails-arrive.md around lines 1 - 11, The changeset front
matter lists many platform-specific CLI packages (e.g.
"@biomejs/cli-darwin-arm64", "@biomejs/cli-linux-x64",
"@biomejs/cli-win32-arm64") but the guideline requires only the main package;
update .changeset/evil-snails-arrive.md by removing all the "@biomejs/cli-*"
entries and leave a single entry: "@biomejs/biome": patch so the front matter
only references the main package.

13-13: Issue link format deviates slightly from the guideline.

The coding guideline specifies Fixed [#NUMBER](issue link): ... format, but you've used [biomejs/biome-vscode#959]. Since the issue is in a different repository, the full reference is arguably clearer, though it doesn't strictly match the guideline pattern.

As per coding guidelines: "For bug fix changesets, start with 'Fixed [#NUMBER](issue link): ...' format".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/evil-snails-arrive.md at line 13, Update the changeset header to
follow the project's guideline by replacing the nonconforming "Fixed
[biomejs/biome-vscode#959](https://github.com/biomejs/biome-vscode/issues/959):
LSP now correctly resolves project directory when `configurationPath` points to
a configuration file outside the workspace." with the required "Fixed
[`#959`](https://github.com/biomejs/biome-vscode/issues/959): ..." format so the
entry starts with "Fixed [`#959`](...):" while preserving the rest of the message
and link; locate the string "Fixed [biomejs/biome-vscode#959]" to change it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.changeset/evil-snails-arrive.md:
- Around line 1-11: The changeset front matter lists many platform-specific CLI
packages (e.g. "@biomejs/cli-darwin-arm64", "@biomejs/cli-linux-x64",
"@biomejs/cli-win32-arm64") but the guideline requires only the main package;
update .changeset/evil-snails-arrive.md by removing all the "@biomejs/cli-*"
entries and leave a single entry: "@biomejs/biome": patch so the front matter
only references the main package.
- Line 13: Update the changeset header to follow the project's guideline by
replacing the nonconforming "Fixed
[biomejs/biome-vscode#959](https://github.com/biomejs/biome-vscode/issues/959):
LSP now correctly resolves project directory when `configurationPath` points to
a configuration file outside the workspace." with the required "Fixed
[`#959`](https://github.com/biomejs/biome-vscode/issues/959): ..." format so the
entry starts with "Fixed [`#959`](...):" while preserving the rest of the message
and link; locate the string "Fixed [biomejs/biome-vscode#959]" to change it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8b486e51-16f3-4330-a658-e4d6ac33a360

📥 Commits

Reviewing files that changed from the base of the PR and between c95ea96 and 2aaaff3.

📒 Files selected for processing (1)
  • .changeset/evil-snails-arrive.md

@g-ortuno
Copy link
Copy Markdown
Contributor Author

g-ortuno commented Mar 8, 2026

@ematipico could you take a look?

} else {
path.to_path_buf()
}
// Collect workspace folder roots as absolute Utf8PathBufs.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert all the code you added here. This isn't the right place. The code we had was only meant to normalize file VS folder.

There's method called resolve_configuration_path, that's where we want to add your fix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a look! How would we incorporate this logic into resolve_configuration_path? The comment for the method says "Resolves the user-provided configurationPath setting to an absolute path.", so returning the config path directly if the path is absolute makes sense to me.

The issue, as far as I can tell, it's not that we're resolving the config path incorrectly, rather than in this method, load_biome_configuration_file_internal, we're using the config path to open the project further below.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semantics of ConfigurationPathHint::FromUser is "take the path as it is (folder/file) and attempt to load it". No further manipulations should be done, as per semantics.

You might want to consider another variant for ConfigurationPathHint, so that we can explictily give proper semantics, and they don't clash with the existing ones.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double-checking if I understand the suggestion:

  1. We would change resolve_configuration_path() to return a new ConfigurationPathHint::FromUserExternal(config_path, project_root) for external configs.
  2. In load_biome_configuration_file_internal, we'll use the FromUserExternal variant to load the config with config_path and open the project with project_root.

Is that roughly what you had in mind?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that should do it.

So the function resolve_configuration_path() returns the new variant only if the path is outside of any working directory (workspace and base_path). Like the case we're trying to solve.

However I don't think the new variant needs to hold the project root. We already have the project root. We're only interested in loading the configuration from a different path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have the project root.

Would this be fs.working_directory()?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, I can't remember at the moment. I believe it should be base_path or the current workspace directory. The key point is that the user opens the project at /Users/eva/www/project1/, while the configuration is at /var/common/biome.json. So we read the configuration at /var/common/biome.json but the project is still in the directory /Users/eva/www/project1/

Copy link
Copy Markdown
Contributor Author

@g-ortuno g-ortuno Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok! Ran out of time to clean things up and write a test for relative paths, but the current code compiles and the test passes. Let me know if you had something else in mind! I'll do another pass and write a test tomorrow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, cleaned things up and added a test for relative external configs. I want to add one more test for workspaces, but please have a look in the meantime!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
.changeset/evil-snails-arrive.md (1)

5-5: ⚠️ Potential issue | 🟡 Minor

Use the required [#NUMBER](issue link) format.

Tiny changeset nit: the prose is fine, but the link text should be just the issue number, e.g. Fixed [#959](...), not Fixed [biomejs/biome-vscode#959](...). House style wins again.

Suggested tweak
-Fixed [biomejs/biome-vscode#959](https://github.com/biomejs/biome-vscode/issues/959): LSP now correctly resolves project directory when `configurationPath` points to a configuration file outside the workspace.
+Fixed [`#959`](https://github.com/biomejs/biome-vscode/issues/959): LSP now correctly resolves project directory when `configurationPath` points to a configuration file outside the workspace.

As per coding guidelines: "For bug fix changesets, start with 'Fixed [#NUMBER](issue link): ...' format".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/evil-snails-arrive.md at line 5, Replace the link text in the
changeset intro so it matches the required format: find the string "Fixed
[biomejs/biome-vscode#959](https://github.com/biomejs/biome-vscode/issues/959):
..." and change it to "Fixed
[`#959`](https://github.com/biomejs/biome-vscode/issues/959): ..." so the link
text is just the issue number as required by the bug-fix changeset style.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In @.changeset/evil-snails-arrive.md:
- Line 5: Replace the link text in the changeset intro so it matches the
required format: find the string "Fixed
[biomejs/biome-vscode#959](https://github.com/biomejs/biome-vscode/issues/959):
..." and change it to "Fixed
[`#959`](https://github.com/biomejs/biome-vscode/issues/959): ..." so the link
text is just the issue number as required by the bug-fix changeset style.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b9113a6b-9e47-4b68-9b70-f0e20fb44f80

📥 Commits

Reviewing files that changed from the base of the PR and between 2aaaff3 and cab99de.

📒 Files selected for processing (1)
  • .changeset/evil-snails-arrive.md

@github-actions github-actions bot added the A-Project Area: project label Mar 10, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/biome_lsp/src/session.rs (1)

719-759: ⚠️ Potential issue | 🟠 Major

Keep in-workspace configurationPaths on the FromUser path.

Line 719 only checks workspace_folders, and Lines 752-759 always return FromUserExternal. That means an in-project config can be misclassified as external when the client only sends rootUri, or when start-up resolves a relative path against the first workspace. Downstream, that flips project resolution from the config’s parent to base_path()/cwd, which is a regression for existing in-workspace configurationPaths. Please only emit FromUserExternal once the resolved absolute path actually sits outside every workspace root and base_path().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_lsp/src/session.rs` around lines 719 - 759, The code currently
emits ConfigurationPathHint::FromUserExternal in several fallbacks without
verifying the resolved absolute path is actually outside every workspace root
and the server base_path; update the logic around config_path resolution
(symbols: config_path, workspace_roots, file_path, root.join(...),
ConfigurationPathHint::FromUser, ConfigurationPathHint::FromUserExternal,
self.base_path()) so that every time you resolve an absolute path (whether from
an absolute config_path, joined against the best-matching workspace root,
against workspace_roots.first(), or against self.base_path()), you check if that
resolved absolute path starts_with any workspace_roots or starts_with the
base_path() and only return FromUserExternal when it truly sits outside all
workspace roots and base_path; otherwise return FromUser. Ensure the file-path
branch (the joined variable) and the fallback branches use this same containment
test before choosing FromUser vs FromUserExternal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_lsp/src/session.rs`:
- Around line 1050-1055: The ConfigurationPathHint::FromUserExternal branch
currently chooses base_path() or fs.working_directory() and skips workspace
folders; change it to prefer the first workspace folder if present (e.g., check
self.workspace_folders or the same accessor used elsewhere), then fall back to
self.base_path(), and finally to fs.working_directory() so external configs use
the workspace root and did_open can locate files correctly; update the branch in
session.rs where ConfigurationPathHint::FromUserExternal is matched to implement
that ordering.

---

Outside diff comments:
In `@crates/biome_lsp/src/session.rs`:
- Around line 719-759: The code currently emits
ConfigurationPathHint::FromUserExternal in several fallbacks without verifying
the resolved absolute path is actually outside every workspace root and the
server base_path; update the logic around config_path resolution (symbols:
config_path, workspace_roots, file_path, root.join(...),
ConfigurationPathHint::FromUser, ConfigurationPathHint::FromUserExternal,
self.base_path()) so that every time you resolve an absolute path (whether from
an absolute config_path, joined against the best-matching workspace root,
against workspace_roots.first(), or against self.base_path()), you check if that
resolved absolute path starts_with any workspace_roots or starts_with the
base_path() and only return FromUserExternal when it truly sits outside all
workspace roots and base_path; otherwise return FromUser. Ensure the file-path
branch (the joined variable) and the fallback branches use this same containment
test before choosing FromUser vs FromUserExternal.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 12731298-7c14-4a4b-82a7-f1ba6c9e0c87

📥 Commits

Reviewing files that changed from the base of the PR and between cab99de and c38ac27.

📒 Files selected for processing (4)
  • crates/biome_configuration/src/lib.rs
  • crates/biome_lsp/src/handlers/text_document.rs
  • crates/biome_lsp/src/session.rs
  • crates/biome_service/src/configuration.rs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
crates/biome_lsp/src/handlers/text_document.rs (1)

45-53: ⚠️ Potential issue | 🟠 Major

Don't make external configs guess the project root.

This branch has the document path, but it only passes the config hint downstream. For FromUserExternal, that means the loader has to guess a root later, so a shared config can still register the wrong project in multi-root workspaces.

Please keep the per-file project path alongside the hint here, rather than recomputing it later from session-wide fallbacks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_lsp/src/handlers/text_document.rs` around lines 45 - 53, The
code calls session.resolve_configuration_path(...) and then only passes the
config hint into session.load_biome_configuration_file(...), which forces
FromUserExternal to guess the project root later; instead preserve and pass the
per-file project path (the resolved document/project path) alongside the
configuration hint to load_biome_configuration_file (or its caller) so the
loader can use the explicit per-file project path rather than recomputing via
session-wide fallbacks; update the call sites around
session.resolve_configuration_path, session.load_biome_configuration_file, and
any FromUserExternal loader invocation to accept and propagate the per-file
project path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_lsp/src/session.rs`:
- Around line 719-725: The closure hint_for_path currently only checks
workspace_roots when deciding between ConfigurationPathHint::FromUser and
::FromUserExternal, which mislabels paths under base_path() in single-root
sessions; update the predicate to consider base_path() as a fallback workspace
root (e.g., treat path as "FromUser" if it starts_with any workspace_roots OR
starts_with(base_path())), referencing the hint_for_path closure,
workspace_roots, base_path(), and the ConfigurationPathHint variants to locate
where to change the check.

---

Duplicate comments:
In `@crates/biome_lsp/src/handlers/text_document.rs`:
- Around line 45-53: The code calls session.resolve_configuration_path(...) and
then only passes the config hint into
session.load_biome_configuration_file(...), which forces FromUserExternal to
guess the project root later; instead preserve and pass the per-file project
path (the resolved document/project path) alongside the configuration hint to
load_biome_configuration_file (or its caller) so the loader can use the explicit
per-file project path rather than recomputing via session-wide fallbacks; update
the call sites around session.resolve_configuration_path,
session.load_biome_configuration_file, and any FromUserExternal loader
invocation to accept and propagate the per-file project path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b3a12afd-55ff-4f42-9c81-3bac9aed2eba

📥 Commits

Reviewing files that changed from the base of the PR and between c38ac27 and 8bcd697.

📒 Files selected for processing (3)
  • crates/biome_lsp/src/handlers/text_document.rs
  • crates/biome_lsp/src/server.tests.rs
  • crates/biome_lsp/src/session.rs

Comment on lines +719 to +725
let hint_for_path = |path: Utf8PathBuf| {
Some(if workspace_roots.iter().any(|r| path.starts_with(r)) {
ConfigurationPathHint::FromUser(path)
} else {
ConfigurationPathHint::FromUserExternal(path)
})
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Treat base_path() as the fallback workspace root.

When workspace_folders is empty, this closure still only checks workspace_roots, so any path resolved under base_path() is mislabelled FromUserExternal. That flips valid rootUri-relative configurationPaths onto the external path in single-root sessions.

💡 Suggested tweak
+        let fallback_root = if workspace_roots.is_empty() {
+            self.base_path()
+        } else {
+            None
+        };
+
         let hint_for_path = |path: Utf8PathBuf| {
-            Some(if workspace_roots.iter().any(|r| path.starts_with(r)) {
+            let is_internal = workspace_roots.iter().any(|root| path.starts_with(root))
+                || fallback_root
+                    .as_ref()
+                    .is_some_and(|root| path.starts_with(root));
+
+            Some(if is_internal {
                 ConfigurationPathHint::FromUser(path)
             } else {
                 ConfigurationPathHint::FromUserExternal(path)
             })
         };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let hint_for_path = |path: Utf8PathBuf| {
Some(if workspace_roots.iter().any(|r| path.starts_with(r)) {
ConfigurationPathHint::FromUser(path)
} else {
ConfigurationPathHint::FromUserExternal(path)
})
};
let fallback_root = if workspace_roots.is_empty() {
self.base_path()
} else {
None
};
let hint_for_path = |path: Utf8PathBuf| {
let is_internal = workspace_roots.iter().any(|root| path.starts_with(root))
|| fallback_root
.as_ref()
.is_some_and(|root| path.starts_with(root));
Some(if is_internal {
ConfigurationPathHint::FromUser(path)
} else {
ConfigurationPathHint::FromUserExternal(path)
})
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_lsp/src/session.rs` around lines 719 - 725, The closure
hint_for_path currently only checks workspace_roots when deciding between
ConfigurationPathHint::FromUser and ::FromUserExternal, which mislabels paths
under base_path() in single-root sessions; update the predicate to consider
base_path() as a fallback workspace root (e.g., treat path as "FromUser" if it
starts_with any workspace_roots OR starts_with(base_path())), referencing the
hint_for_path closure, workspace_roots, base_path(), and the
ConfigurationPathHint variants to locate where to change the check.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@g-ortuno I think this is relevant. What do you think?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I look at this the more confused I get 😵‍💫. I'm not sure what the right behavior should be here. Imagine:

configuration path: "configs/biome.js"
base_path(): c:/project/

Resolved path: c:/project/configs/biome.js

The resolved path is inside base_path.

If we return FromUser(), then the project root will be c:/project/configs. If we return FromUserExternal(), then the project root will be c:/project/. So technically, the config is not external, but the chosen project root is correct.

I'm inclined to keep returning FromUserExternal() here. I can follow up with closely analyzing all the cases (I counted 15!), and proposing a more hollistic fix. What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah let's keep it as is. We'll keep it monitored! thank you very much for the fix, lot of users will be grateful

Copy link
Copy Markdown
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good! Just one minor comment to address

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 11, 2026

Merging this PR will not alter performance

✅ 1 untouched benchmark
⏩ 215 skipped benchmarks1


Comparing g-ortuno:config-in-separate-dir (8bcd697) with main (8475d90)2

Open in CodSpeed

Footnotes

  1. 215 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (71c7df6) during the generation of this report, so 8475d90 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@ematipico ematipico merged commit b881fea into biomejs:main Mar 12, 2026
16 checks passed
@github-actions github-actions bot mentioned this pull request Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-LSP Area: language server protocol A-Project Area: project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Biome VS Code Extension Configuration Issue when format on save Biome settings file doesn't seem to work

2 participants